home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / BARNET / COMPILER / SATHER / !Sather / Library / Graphs / sa / digrph_alg < prev    next >
Text File  |  1996-07-13  |  8KB  |  197 lines

  1. ---------------------------> Sather 1.1 source file <--------------------------
  2. -- Author: Benedict A. Gomes <gomes@samosa.ICSI.Berkeley.EDU>
  3. -- COPYRIGHT NOTICE: This code is provided WITHOUT ANY WARRANTY
  4. -- and is subject to the terms of the SATHER LIBRARY GENERAL PUBLIC
  5. -- LICENSE contained in the file: Sather/Doc/License of the
  6. -- Sather distribution. The license is also available from ICSI,
  7. -- 1947 Center St., Suite 600, Berkeley CA 94704, USA.
  8. -------------------------------------------------------------------
  9. class DIGRAPH_ALG{NTP} is 
  10.    -- The default version of the algorithm class expects node indices
  11.    -- of type NTP and will any kind of read-only directed graph.
  12.    -- Using this version simplifies some uses but can be significantly
  13.    -- less efficient
  14.    include DIGRAPH_ALG{INT,$RO_DIGRAPH{INT}};
  15.    
  16. end;
  17. -------------------------------------------------------------------
  18. class DIGRAPH_ALG{NTP, GTP < $RO_DIGRAPH{NTP}} is 
  19.    -- NTP is the node index type and GTP is the graph type
  20.    --  
  21.    -- A collection of some very simple graph traversal algorithms
  22.    -- 
  23.    -- All the following routines assume that the graph is acyclic.
  24.    -- They may go into an infinite loop or fail in some other way if
  25.    -- the graph contains cycles. 
  26.    -- Usage:
  27.    --     g: DIGRAPH{INT} := #;
  28.    --     n1 ::= g.add_node(1);
  29.    --     n2 ::= g.add_node(2);
  30.    --     n3 ::= g.add_node(3)
  31.    --     g.connect(n1,n2); g.connect(n1,n3);
  32.    --     Constructs:
  33.    --      1
  34.    --      /\
  35.    --     2  3
  36.    --
  37.    --     Getting the layers of the graph:
  38.    --        l: LIST{SET{INT}} := #;
  39.    --        loop s: SET{INT} := DIGRAPH_ALG::layer!(g);
  40.    --             l := l.append(s);
  41.    --         end;
  42.  
  43.    source!(once g: GTP): NTP is
  44.       -- Yield all source nodes in the graph "g"
  45.       loop n ::= g.node!;  if g.n_incoming(n) = 0 then yield n end; end;
  46.    end;
  47.       
  48.    sink!(once g: GTP): NTP is
  49.       -- Yield all sink nodes in the graph "g"
  50.       loop n ::= g.node!;  if g.n_outgoing(n) = 0 then yield n end; end;
  51.    end;
  52.    
  53.    bf!(once g: GTP,once n: NTP): NTP is
  54.       -- Return all nodes reachable from "n" in breadth first order
  55.       -- With inout arguments, also return the depth of the node
  56.       depths: FMAP{NTP,INT} := #;
  57.       q: A_QUEUE{NTP} := #;
  58.       q.enq(n);
  59.       depths := depths.insert(n,0);
  60.       loop until!(q.is_empty);
  61.      current ::= q.remove;
  62.      node_depth: INT := depths.get(current);
  63.      yield current;
  64.      child_node_depth: INT := node_depth + 1;
  65.      loop out_node ::= g.outgoing!(current);
  66.         if ~depths.has_ind(out_node) then 
  67.            q.enq(out_node); 
  68.            depths:=depths.insert(out_node,child_node_depth); 
  69.         end;
  70.      end;
  71.       end;
  72.    end;
  73.  
  74.    df!(once g: GTP,once n: NTP): NTP is
  75.       -- Return all nodes reachable from "n" in depth first order
  76.       stack ::= #FLIST{NTP};  stack := stack.push(n);
  77.       depths  ::= #FMAP{NTP,INT};   depths := depths.insert(n,0);       
  78.       loop until!(stack.is_empty);
  79.      cur ::= stack.pop;     
  80.      cur_depth: INT := depths.get(cur);
  81.      yield cur;        -- Actual visit
  82.      child_depth: INT := cur_depth+1;
  83.      loop neigh ::= g.outgoing!(cur);
  84.         if ~depths.has_ind(neigh) then 
  85.            stack:=stack.push(neigh);
  86.            depths:=depths.insert(neigh,child_depth);
  87.         end; -- else ( Neighbor has been seen before => backedge.) 
  88.      end;
  89.       end; -- Until stack is empty
  90.    end;
  91.  
  92.    topo_order!(once g: GTP): NTP is
  93.       -- Yield nodes in topological order
  94.       if g.is_empty then quit end; 
  95.       -- Current indegree holds the current number of incoming per node
  96.       -- When the number of incoming goes to zero, the node is
  97.       -- visited and the current indegree values of all its outgoing  are
  98.       -- decremented.
  99.       current_indeg ::= #FMAP{NTP,INT}; 
  100.       -- Mapping from nodes to living incoming
  101.       roots ::= #FLIST{NTP};    -- Root nodes
  102.       loop n: NTP := g.node!;
  103.      deg ::= g.n_incoming(n);
  104.      current_indeg := current_indeg.insert(n,deg);
  105.      if (deg = 0) then roots := roots.push(n); end;
  106.       end;
  107.       loop while!(roots.size > 0);
  108.      ni ::= roots.pop;
  109.      -- Yield the next node in topological order
  110.      yield(ni);
  111.      loop out_node ::= g.outgoing!(ni);
  112.         outgoing_indegree ::= current_indeg.get(out_node);
  113.         new_out_indeg ::= outgoing_indegree - 1;
  114.         current_indeg:=current_indeg.insert(out_node,new_out_indeg);
  115.         if (new_out_indeg=0) then roots := roots.push(out_node);  end;
  116.      end;
  117.       end;
  118.    end;
  119.  
  120.    is_topo_order(g: GTP,nodes: $ARR{NTP}): BOOL is
  121.       -- Verify that the nodes in "node_order" are in topological order,
  122.       -- that each node's order is greater than the order of any
  123.       -- of its parents. Better methods probably exist...
  124.       node_order ::= #FMAP{NTP,INT};
  125.       loop node_order:=node_order.insert(nodes.elt!, nodes.size.times!) end;
  126.       loop
  127.      node_depth_tup: TUP{NTP,INT} := node_order.pair!;
  128.      child: NTP:=node_depth_tup.t1; child_order: INT := node_depth_tup.t2;
  129.      if g.has_node(child) then
  130.         loop parent: NTP := g.incoming!(child);
  131.            if node_order.has_ind(parent) then
  132.           parent_order: INT := node_order.get(parent);
  133.           if parent_order >= child_order then
  134.              return false;
  135.           end;
  136.            end; -- Ignore the parent if it is not in the order
  137.         end; -- End of loop on parents
  138.      end; -- End of if has_node(child).
  139.      -- If the child is not in the current graph, also ignore it
  140.       end;
  141.       return true;
  142.    end;
  143.  
  144.    layer!(once g: GTP): SET{NTP} is
  145.       -- Return the "layers" of the graph, i.e. peel off successive root
  146.       -- sets
  147.       -- Current indegree holds the current number of incoming per node
  148.       -- When the number of incoming goes to zero, the node is
  149.       -- visited and the current indegree values of all its outgoing  are
  150.       -- decremented.
  151.       -- All nodes/edges start out live. 
  152.       -- Loop, at each iteration:
  153.       --    Find the nodes that have no live incoming edges and  yield them
  154.       --    Mark the nodes and all edges going out of them as dead
  155.       -- Until no more nodes are left alive
  156.       
  157.       li::= #FMAP{NTP,INT}; -- Maps nodes to the number of live incoming edges
  158.       dead ::= #SET{NTP};      -- Root nodes, current dead set
  159.       nodes_left: INT := g.n_nodes; -- Number of live nodes left
  160.       loop n: NTP := g.node!;
  161.      deg ::= g.n_incoming(n);
  162.      li := li.insert(n,deg);
  163.      if (deg = 0) then dead.insert(n); end;
  164.       end;
  165.       yield dead;
  166.       nodes_left := nodes_left - dead.size;
  167.       new_dead: SET{NTP};
  168.       loop while!(nodes_left > 0);
  169.      new_dead := #SET{NTP};
  170.      loop r ::= dead.elt!;
  171.         -- Indicate that the outgoing edges from the old root set 
  172.         -- are now dead
  173.         loop out_node ::= g.outgoing!(r);
  174.            -- Get the current live indegree of the node "out_node"
  175.            indeg_of_out_node: INT := li.get(out_node);
  176.            -- Reduce the number of "live"  edges into "out_node"
  177.            -- by 1 since "r", a parent root,  has been yielded and is dead
  178.            new_live_indegree_of_out_node ::= indeg_of_out_node - 1;
  179.            li := li.insert(out_node,new_live_indegree_of_out_node);
  180.            -- If there are no more "live" incoming edges to "out_node",
  181.            -- yield it. Out_node is now dead
  182.            if new_live_indegree_of_out_node = 0 then
  183.           new_dead.insert(out_node);
  184.            end;
  185.         end; -- End of loop around outgoing nodes of a root
  186.      end; -- End of loop through old dead
  187.      yield new_dead;
  188.      nodes_left := nodes_left - new_dead.size;
  189.      dead := new_dead;
  190.      -- Note: We cannot re-use the old dead's space since it might
  191.      -- be in use outside this routine
  192.       end;
  193.    end;
  194.  
  195. end;
  196. -------------------------------------------------------------------
  197.